import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
import { ElMessage } from 'element-plus';
import { getToken, cleanSession } from '@/utils/auth';

//返回值类型
export interface Result<T = any> {
    code: number;
    message: string;
    data?: T;
}
//返回的状态码
export enum StatusCode {
    NoAuth = 600,//无权限
    Success = 20000 //返回成功
}

/**
 *比较两个对象是否相等
 * @method isObjectValueEqual
 * @param {Object} a 对象a
 * @param {Object} b 对象b
 * @return {Boolean}
 */
function isObjectValueEqual(a: any, b: any) {
    // 判断两个对象是否指向同一内存，指向同一内存返回true,同时比较null和undefined情况
    if (a == b) return true;
    if (a == null || a == undefined || b == null || b == undefined) {
        return false;
    }
    // 获取两个对象键值数组
    let aProps = Object.getOwnPropertyNames(a);
    let bProps = Object.getOwnPropertyNames(b);
    // 判断两个对象键值数组长度是否一致，不一致返回false
    if (aProps.length !== bProps.length) return false;
    // 遍历对象的键值
    for (let prop in a) {
        // 判断a的键值，在b中是否存在，不存在，返回false
        if (b.hasOwnProperty(prop)) {
            // 判断a的键值是否为对象，是则递归，不是对象直接判断键值是否相等，不相等返回false
            if (typeof a[prop] === "object") {
                if (!isObjectValueEqual(a[prop], b[prop])) return false;
            } else if (a[prop] !== b[prop]) {
                return false;
            }
        } else {
            return false;
        }
    }
    return true;
}

let pending = [] as any  //声明一个数组用于存储每个ajax请求的取消函数和ajax标识
let cancelToken = axios.CancelToken;
let removeRepeatUrl = (ever: any) => {
    for (let p in pending) {

        // 判断是否存在重复请求
        if (
            pending[p].config &&
            pending[p].config.url === ever.url &&
            pending[p].config.method === ever.method
        ) {
            if (isObjectValueEqual(pending[p].config, ever))
                //当当前请求在数组中存在时执行函数体
                pending[p].cancle(); //执行取消操作

            pending.splice(p, 1); //把这条记录从数组中移除
        }
    }
};


class request {
    private instance: AxiosInstance; //axios实例
    //构造函数 给instance进行初始化
    constructor(config: AxiosRequestConfig) {
        //创建axios实例

        this.instance = axios.create(config);
        //拦截器配置
        this.interceptors();
    }

    //拦截器
    private interceptors() {


        //请求发送之前的拦截器：添加token
        this.instance.interceptors.request.use((config: any) => {
            //配置token

            removeRepeatUrl({
                method: config.method,
                url: config.url,
                params: config.params,
                data: config.data
            });
            // 创建cancleToken和cancle取消请求方法, 每个请求都不一样的哦
            config.cancelToken = new cancelToken(c => {
                // 自定义唯一标识
                pending.push({
                    config: {
                        method: config.method,
                        url: config.url,
                        params: config.params,
                        data: config.data
                    },
                    cancle: c // 
                });
            });

            let token = getToken();
            if (token) {
                config.headers = {
                    ...config.headers,
                    Authorization: `Bearer ${token}`
                }
            }

            return config;
        }, (error: any) => {
            // 错误抛到业务代码
            error.data = {}
            error.data.message = '服务器异常，请联系管理员！'
            return error
        })

        //请求返回之后的处理
        this.instance.interceptors.response.use((res: any) => {
            removeRepeatUrl({
                method: res.config.method,
                url: res.config.url,
                params: res.config.params,
                data: res.config.data
            });
            //在一个ajax响应后再执行一下取消操作，把已经完成的请求从pending中移除
            if (res && res.data) {
                const data = res.data as any;
                if (data.code == StatusCode.NoAuth) { //token过期

                } else if (data.code == StatusCode.Success || res.config.responseType == 'arraybuffer') { //返回成功
                    return res
                } else if (data.code == 403) {
                    ElMessage.error(data.message || '服务器出错!')

                } else if (data.code == 401) {

                    window.location.href = "/login";
                    cleanSession()
                    ElMessage.error(data.message)
                } else {
                    ElMessage.error(data.message || '服务器出错!')
                    return res || null;
                }
            }
        }, (error) => {

            console.log('进入错误====》》》数据判断', error)

            if (error && error.response) {
                error.data = {};
                switch (error.response.status) {
                    case 400:
                        error.data.message = '错误请求';
                        ElMessage.error(error.data.message)
                        break
                    case 401:
                        error.data.message = '未授权，请重新登录';
                        window.location.href = "/login";
                        cleanSession()
                        ElMessage.error(error.data.message)
                        break
                    case 403:
                        console.log(error.data);

                        error.data.message = '拒绝访问';
                        ElMessage.error(error.data.message)
                        break
                    case 404:
                        error.data.message = '请求错误,未找到该资源';
                        ElMessage.error(error.data.message)
                        break
                    case 405:
                        error.data.message = '请求方法未允许';
                        ElMessage.error(error.data.message)
                        break
                    case 408:
                        error.data.message = '请求超时';
                        ElMessage.error(error.data.message)
                        break
                    case 500:
                        error.data.message = '服务器端出错';
                        ElMessage.error(error.data.message)
                        break
                    case 501:
                        error.data.message = '网络未实现';
                        ElMessage.error(error.data.message)
                        break
                    case 502:
                        error.data.message = '网络维护中';
                        ElMessage.error(error.data.message)
                        break
                    case 503:
                        error.data.message = '服务不可用';
                        ElMessage.error(error.data.message)
                        break
                    case 504:
                        error.data.message = '网络超时';
                        ElMessage.error(error.data.message)
                        break
                    case 505:
                        error.data.message = 'http版本不支持该请求';
                        ElMessage.error(error.data.message)
                        break
                    default:
                        error.data.message = `连接错误${error.response.status}`;
                        ElMessage.error(error.data.message)
                }
            } else {
                // error.message = "连接到服务器失败";
                // "ERR_BAD_RESPONSE"
                if (error.code == 'ERR_CANCELED') {
                    // ElMessage.error("系统维护中")

                } else {
                    ElMessage.error("系统维护中")
                    console.log(error, "http");
                }

            }
            return Promise.reject(error)
        })
    }
    //参数的处理
    getParms(parms: any) {
        let _parms = '';
        if (Object.is(parms, undefined || null)) {
            _parms = '';
        }
        else {
            for (const key in parms) {
                if (parms.hasOwnProperty(key) && parms[key]) {
                    _parms += `${parms[key]}/`
                }
            }
        }
        if (_parms) _parms = _parms.slice(0, _parms.length - 1);
        return _parms;
    }
    //  {userId : 10} 有参数 和没有参数 怎么处理
    //http:localhost:8080/api/getUserByid?userId=10
    get<T = any>(url: string, parms?: any): Promise<Result<T>> {
        return new Promise((resolve, reject) => {
            this.instance.get<T>(url, {
                params: parms,

                paramsSerializer: (params) => {
                    const searchParams = new URLSearchParams();
                    for (const key in params) {
                        if (params.hasOwnProperty(key)) {
                            searchParams.append(key, params[key]);
                        }
                    }
                    return searchParams.toString();
                }
                ,
            }).then((res) => {
                resolve(res.data as any)
            }).catch((error) => {
                reject(error)
            })
        })
    }
    getImages<T = any>(url: string, parms?: any): Promise<Result<T>> {
        return new Promise((resolve, reject) => {
            this.instance.get<T>(url, {
                params: parms,

                paramsSerializer: (params) => {
                    const searchParams = new URLSearchParams();
                    for (const key in params) {
                        if (params.hasOwnProperty(key)) {
                            searchParams.append(key, params[key]);
                        }
                    }
                    return searchParams.toString();
                },
                responseType: 'arraybuffer'
            }).then((res) => {
                resolve(res.data as any)
            }).catch((error) => {
                reject(error)
            })
        })
    }



    // {userId : 10,useName : 'test'}  ==>  10/test
    //http:localhost:8080/api/getUserByid/10/test
    getRestApi<T = any>(url: string, parms?: any): Promise<Result<T>> {
        return new Promise((resolve, reject) => {
            this.instance.get<T>(this.getParms(parms) ? `${url}/${this.getParms(parms)}` : url)
                .then((res) => {
                    resolve(res.data as any)
                }).catch((error) => {
                    reject(error)
                })
        })
    }
    //获取验证码
    getImage(url: string, params: any) {
        return this.instance.post(url, params, {
            responseType: 'arraybuffer'
        })
    }
    filePost<T = any>(url: string, parms: any): Promise<Result<T>> {
        return new Promise((resolve, reject) => {
            this.instance.post(url, parms, {
                headers: {
                    'Content-Type': 'multipart/form-data'
                }
            }).then((res) => {
                resolve(res.data as any)
            }).catch((error) => {
                reject(error)
            })
        })
    }
    newPost<T = any>(url: string, parms: any): Promise<Result<T>> {
        return new Promise((resolve, reject) => {
            this.instance.post(url, parms, {
                headers: {
                    'Content-Type': 'multipart/form-data'
                }
            }).then((res) => {
                resolve(res.data as any)
            }).catch((error) => {
                reject(error)
            })
        })
    }
    //post请求
    post<T = any>(url: string, parms?: any): Promise<Result<T>> {
        return new Promise((resolve, reject) => {
            this.instance.post(url, parms, {
                transformRequest: [(params) => {
                    return JSON.stringify(params)
                }],
                headers: {
                    'Content-Type': 'application/json'
                }
            }).then((res) => {
                resolve(res.data as any)
            }).catch((error) => {
                reject(error)
            })
        })
    }
    //post请求
    postParams<T = any>(url: string, parms?: any): Promise<Result<T>> {
        return new Promise((resolve, reject) => {
            this.instance.post(url, parms, {
                transformRequest: [(params) => {
                    return JSON.stringify(params)
                }],
                headers: {
                    'Content-Type': 'application/json'
                },
                responseType: 'arraybuffer'
            }).then((res) => {
                resolve(res.data as any)
            }).catch((error) => {
                reject(error)
            })
        })
    }

    //put请求
    put<T = any>(url: string, parms?: any): Promise<Result<T>> {
        return new Promise((resolve, reject) => {
            this.instance.put(url, parms, {
                transformRequest: [(params) => {
                    return JSON.stringify(params)
                }],
                headers: {
                    'Content-Type': 'application/json'
                }
            }).then((res) => {
                resolve(res.data as any)
            }).catch((error) => {
                reject(error)
            })
        })
    }
    //删除
    // {userId : 10}  ==>  10
    //http:localhost:8080/api/deleteById/10
    delete<T = any>(url: string, parms?: any): Promise<Result<T>> {
        return new Promise((resolve, reject) => {
            this.instance.delete<T>(this.getParms(parms) ? `${url}/${this.getParms(parms)}` : url)
                .then((res) => {
                    resolve(res.data as any)
                }).catch((error) => {
                    reject(error)
                })
        })
    }
    // {id:10,user:10}-->?id=10&user=10
    deleteApi<T = any>(url: string, parms?: any): Promise<Result<T>> {
        return new Promise((resolve, reject) => {
            this.instance.delete<T>(url, {
                params: parms,
                paramsSerializer: (params) => {
                    const searchParams = new URLSearchParams();
                    for (const key in params) {
                        if (params.hasOwnProperty(key)) {
                            searchParams.append(key, params[key]);
                        }
                    }
                    return searchParams.toString();
                }


            }).then((res) => {
                resolve(res.data as any)
            }).catch((error) => {
                reject(error)
            })
        })
    }
    // 传递对象给后端 delete data
    deleteData<T = any>(url: string, parms?: any): Promise<Result<T>> {
        return new Promise((resolve, reject) => {
            this.instance.delete<T>(url, {
                data: parms
            }).then((res) => {
                resolve(res.data as any)
            }).catch((error) => {
                reject(error)
            })
        })
    }
}
export default request;